package com.baidu.iit.pxp.behavior;

import com.baidu.iit.pvm.ActivityException;
import com.baidu.iit.pvm.PvmTransition;
import com.baidu.iit.pvm.delegate.ActivityExecution;
import com.baidu.iit.pvm.delegate.Condition;
import com.baidu.iit.pvm.runtime.InterpretableExecution;
import com.baidu.iit.pxp.parser.BpmnParse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * User: huangweili
 * Date: 14-4-28
 * Time: 下午3:44
 */
public class BpmnActivityBehavior implements Serializable {

    private static final long serialVersionUID = 1L;

    private static Logger log = LoggerFactory.getLogger(BpmnActivityBehavior.class);


    public void performDefaultOutgoingBehavior(ActivityExecution activityExceution) {
        performOutgoingBehavior(activityExceution, true, false, null);
    }

    public void performIgnoreConditionsOutgoingBehavior(ActivityExecution activityExecution) {
        performOutgoingBehavior(activityExecution, false, false, null);
    }


    protected void performOutgoingBehavior(ActivityExecution execution,
                                           boolean checkConditions, boolean throwExceptionIfExecutionStuck, List<ActivityExecution> reusableExecutions) {

        if (log.isDebugEnabled()) {
            log.debug("Leaving activity '{}'", execution.getActivity().getId());
        }

        String defaultSequenceFlow = (String) execution.getActivity().getProperty("default");
        List<PvmTransition> transitionsToTake = new ArrayList<PvmTransition>();

        List<PvmTransition> outgoingTransitions = execution.getActivity().getOutgoingTransitions();
        for (PvmTransition outgoingTransition : outgoingTransitions) {
            if (defaultSequenceFlow == null || !outgoingTransition.getId().equals(defaultSequenceFlow)) {
                Condition condition = (Condition) outgoingTransition.getProperty(BpmnParse.PROPERTYNAME_CONDITION);
                if (condition == null || !checkConditions || condition.evaluate(execution)) {
                    transitionsToTake.add(outgoingTransition);
                }
            }
        }

        if (transitionsToTake.size() == 1) {

            execution.take(transitionsToTake.get(0));

        } else if (transitionsToTake.size() >= 1) {

            execution.inactivate();
            if (reusableExecutions == null || reusableExecutions.isEmpty()) {
                execution.takeAll(transitionsToTake, Arrays.asList(execution));
            } else {
                execution.takeAll(transitionsToTake, reusableExecutions);
            }

        } else {

            if (defaultSequenceFlow != null) {
                PvmTransition defaultTransition = execution.getActivity().findOutgoingTransition(defaultSequenceFlow);
                if (defaultTransition != null) {
                    execution.take(defaultTransition);
                } else {
                    throw new ActivityException("Default sequence flow '" + defaultSequenceFlow + "' could not be not found");
                }
            } else {

                Object isForCompensation = execution.getActivity().getProperty(BpmnParse.PROPERTYNAME_IS_FOR_COMPENSATION);
                if (isForCompensation != null && (Boolean) isForCompensation) {

                    InterpretableExecution parentExecution = (InterpretableExecution) execution.getParent();
                    ((InterpretableExecution) execution).remove();
                    parentExecution.signal("compensationDone", null);

                } else {

                    if (log.isDebugEnabled()) {
                        log.debug("No outgoing sequence flow found for {}. Ending execution.", execution.getActivity().getId());
                    }
                    execution.end();

                    if (throwExceptionIfExecutionStuck) {
                        throw new ActivityException("No outgoing sequence flow of the inclusive gateway '" + execution.getActivity().getId()
                                + "' could be selected for continuing the process");
                    }
                }

            }
        }
    }

}
